# 第02章 MyBatis初始化过程
# 2.1 初始化过程做了什么?
MyBatis的初始化可以有两种方式:
- 基于XML配置文件:基于XML配置文件的方式是将MyBatis的所有配置信息放在XML文件中,MyBatis通过加载并XML配置文件,将配置文信息组装成内部的Configuration对象
- 基于Java API:这种方式不使用XML配置文件,需要MyBatis使用者在Java代码中,手动创建Configuration对象,然后将配置参数set 进入Configuration对象中
本章源码分析是基于XML配置文件方式的MyBatis初始化
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
try {
OtaPackageMapper otaPackageMapper = sqlSession.getMapper(OtaPackageMapper.class);
List<OtaPackage> otaPackages = otaPackageMapper.selectOtaPackageList();
for (OtaPackage otaPackage : otaPackages) {
System.out.println("packageId:"+otaPackage.getPackageId());
}
}catch (Exception e){
e.printStackTrace();
}finally {
sqlSession.close();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
上述语句的作用是执行com.clare.mapper.OtaPackageMapper.selectOtaPackageList 定义的SQL语句,返回一个List结果集。总的来说,上述代码经历了mybatis初始化 -->创建SqlSession -->执行SQL语句 返回结果三个过程。
上述代码的功能是根据配置文件mybatis-config.xml 配置文件,创建SqlSessionFactory对象,然后产生SqlSession,执行SQL语句。而mybatis的初始化就发生在第三句:SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
根据1.1.6节并结合源码分析,MyBatis基本的初始化过程如下(省略掉部分细节):
简单分析过程如下:
- 调用SqlSessionFactoryBuilder对象的build(inputStream)方法;
- SqlSessionFactoryBuilder会根据输入流inputStream等信息创建XMLConfigBuilder对象;
- SqlSessionFactoryBuilder调用XMLConfigBuilder对象的parse()方法;
- XMLConfigBuilder对象返回Configuration对象;
- SqlSessionFactoryBuilder根据Configuration对象创建一个DefaultSessionFactory对象;
- SqlSessionFactoryBuilder返回 DefaultSessionFactory对象给Client,供Client使用。
MyBatis初始化基本过程:
SqlSessionFactoryBuilder根据传入的数据流生成Configuration对象,然后根据Configuration对象创建默认的SqlSessionFactory实例。
首先,给文件mybatis-config.xml创建输入流:
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
2
3
或者使用Reader:
String resource = "mybatis-config.xml";
Reader resourceAsReader = Resources.getResourceAsReader(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsReader);
2
3
底层的代码实现:
public static InputStream getResourceAsStream(String resource) throws IOException {
return getResourceAsStream(null, resource);
}
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
if (in == null) {
throw new IOException("Could not find resource " + resource);
}
return in;
}
public static Reader getResourceAsReader(String resource) throws IOException {
Reader reader;
if (charset == null) {
reader = new InputStreamReader(getResourceAsStream(resource));
} else {
reader = new InputStreamReader(getResourceAsStream(resource), charset);
}
return reader;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
就是将输入的路径转换为一个输入流并返回
SqlSessionFactoryBuilder相关代码如下:
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream,
String environment,
Properties properties) {
try {
// 创建XMLConfigBuilder对象用来解析XML配置文件,生成Configuration对象
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream,
environment,
properties);
// 将XML配置文件内的信息解析成Java对象Configuration对象
Configuration config = parser.parse();
// 根据Configuration对象创建出SqlSessionFactory对象
return build(config);
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
inputStream.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
// 从此处可以看出,MyBatis内部通过Configuration对象来创建SqlSessionFactory
// 用户也可以自己通过API构造好Configuration对象,调用此方法创建SqlSessionFactory
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
// Reader模式实现
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader,
String environment,
Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
上述的初始化过程中,涉及到了以下几个对象:
- SqlSessionFactoryBuilder : SqlSessionFactory的构造器,用于创建SqlSessionFactory,采用了Builder设计模式(第02章介绍)
- Configuration :该对象是mybatis-config.xml文件中所有mybatis配置信息
- SqlSessionFactory:SqlSession工厂类,以工厂形式创建SqlSession对象,采用了Factory工厂设计模式(第03章介绍)
- XmlConfigParser :负责将mybatis-config.xml配置文件解析成Configuration对象,供SqlSessonFactoryBuilder使用,创建SqlSessionFactory
# 2.2 Configuration对象创建
任何框架的初始化,无非是加载自己运行时所需要的配置信息。MyBatis的配置信息,大概包含以下信息,其高层级结构如下:
× configuration 配置
× properties 属性 × settings 设置 ×typeAliases 类型命名 ×typeHandlers 类型处理器 ×objectFactory 对象工厂 ×plugins 插件 ×environments 环境 ×environment 环境变量 ×transactionManager 事务管理器 ×dataSource 数据源 ×映射器
MyBatis的上述配置信息会配置在XML配置文件中,那么,这些信息被加载进入MyBatis内部,MyBatis是怎样维护的呢?
MyBatis采用了一个非常直白和简单的方式---使用org.apache.ibatis.session.Configuration 对象作为一个所有配置信息的容器,Configuration对象的组织结构和XML配置文件的组织结构几乎完全一样(当然,Configuration对象的功能并不限于此,它还负责创建一些MyBatis内部使用的对象,如Executor等,这将在后文中讨论)。如下图所示:
MyBatis根据初始化好Configuration信息,这时候用户就可以使用MyBatis进行数据库操作了。
可以这么说,MyBatis初始化的过程,就是创建 Configuration对象的过程。
接着上述的 MyBatis初始化基本过程讨论,当SqlSessionFactoryBuilder执行build()方法,调用了XMLConfigBuilder的parse()方法,然后返回了Configuration对象。那么parse()方法是如何处理XML文件,生成Configuration对象的呢?
SqlSessionFactoryBuilder:
分析SqlSessionFactoryBuilder执行语句(省去异常捕获):
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream,
String environment,
Properties properties) {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream,
environment,
properties);
Configuration config = parser.parse();
return build(config);
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
build中会根据inputStream流创建XMLConfigBuilder对象:
public XMLConfigBuilder(InputStream inputStream,
String environment,
Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()),
environment,
props);
}
private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
上述过程新建了XMLMapperEntityResolver对象,并根据该对象创建了XPathParser对象,然后调用了私有的构造方法,首先调用了父类BaseBuilder的构造器(XMLConfigBuilder内部含XPathParser对象,BaseBuilder内部含Configuration对象)
public abstract class BaseBuilder {
protected final Configuration configuration;
protected final TypeAliasRegistry typeAliasRegistry;
protected final TypeHandlerRegistry typeHandlerRegistry;
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}
// ...
}
2
3
4
5
6
7
8
9
10
11
12
13
可见新建了Configuration内部对象,而new Configuration()如下:
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
可见使用类型别名注册器和语言注册器注册了一系列的类的别名和language,在mybatis-config.xml中也可以使用别名标签,最终也会调用别名注册器进行注册:
<typeAliases>
<!--自定义定义单个类的别名-->
<typeAlias type="com.clare.model.OtaPackage" alias="otaPackage"/>
<!--通过自动扫描形式自定义别名-->
<package name="com.clare.model"/>
</typeAliases>
2
3
4
5
6
关于类型注册器TypeAliasRegistry下章讲解,主要是往Map中存储key为别名,value为类的全限定路径
创建好了XMLConfigBuilder后,调用其的parse()方法创建Configuration对象:
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
propertiesElement(root.evalNode("properties"));
Properties settings = settingsAsProperties(root.evalNode("settings"));
loadCustomVfs(settings);
loadCustomLogImpl(settings);
typeAliasesElement(root.evalNode("typeAliases"));
pluginElement(root.evalNode("plugins"));
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
reflectorFactoryElement(root.evalNode("reflectorFactory"));
settingsElement(settings);
// read it after objectFactory and objectWrapperFactory issue #631
environmentsElement(root.evalNode("environments"));
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
typeHandlerElement(root.evalNode("typeHandlers"));
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
方法parser.evalNode("/configuration")调用了XPathParser的evalNode(String expression)的方法,用于解析节点。parseConfiguration(XNode root)用于解析XNode设置到Configuration中。最终返回Configuration对象。(随后介绍XPathParser的创建及如何解析XML文件)
最后,SqlSessionFactoryBuilder根据Configuration对象创建默认的SqlSessionFactory返回。